package cn.endeavour.user.service.impl;

import cn.endeavour.basic.exception.BusinessException;
import cn.endeavour.basic.service.impl.BaseServiceImpl;
import cn.endeavour.basic.util.AjaxResult;
import cn.endeavour.basic.util.HttpClientUtils;
import cn.endeavour.basic.util.MD5Utils;
import cn.endeavour.basic.util.StrUtils;
import cn.endeavour.user.constant.UserConstant;
import cn.endeavour.user.constant.WxConstants;
import cn.endeavour.user.domain.LoginInfo;
import cn.endeavour.user.domain.User;
import cn.endeavour.user.domain.WxUser;
import cn.endeavour.user.dto.LoginDto;
import cn.endeavour.user.dto.UserDto;
import cn.endeavour.user.mapper.LoginInfoMapper;
import cn.endeavour.user.mapper.UserMapper;
import cn.endeavour.user.mapper.WxUserMapper;
import cn.endeavour.user.service.ILoginInfoService;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class LoginInfoServiceImpl extends BaseServiceImpl<LoginInfo> implements ILoginInfoService {
    @Autowired
    private LoginInfoMapper logininfoMapper;

    @Autowired
    @Qualifier("redisTemplate")
    private RedisTemplate redisTemplate;

    @Autowired
    private WxUserMapper wxUserMapper;

    @Autowired
    private UserMapper userMapper;


    @Override
    public AjaxResult loginByAccount(LoginDto loginDto) {
//        System.out.println(loginDto);
        // 非空判断
        if (StringUtils.isEmpty(loginDto.getUsername()) || StringUtils.isEmpty(loginDto.getPassword()) || StringUtils.isEmpty(loginDto.getType())) {
            throw new BusinessException("请输入用户名以及密码");
        }
        // 去查询
        LoginInfo loginInfo = logininfoMapper.findByAccount(loginDto);
        // 判断查出来的对象
        if (loginInfo == null) {
            throw new BusinessException("用户名或者密码错误");
        }
        // 校验密码
        String salt = loginInfo.getSalt();
//        System.out.println(salt);
        String password = MD5Utils.encrypByMd5(loginDto.getPassword() + salt);
//        System.out.println(password);
        if (!password.equals(loginInfo.getPassword())) {
            throw new BusinessException("用户名或者密码错误");
        }
        // 判断账号是否被禁用
        if (loginInfo.getDisable() == 0) {
            throw new BusinessException("您的账号已经被封号,请联系管理员");
        }
        // 保存在Redis中
        String token = UUID.randomUUID().toString();
        // 设置过期时间
        redisTemplate.opsForValue().set(token, loginInfo, 30, TimeUnit.MINUTES);
        // 保存在
        HashMap<String, Object> map = new HashMap<>();
        map.put("token", token);
        loginInfo.setPassword("");
        loginInfo.setSalt("");
        map.put("loginInfo", loginInfo);
        return AjaxResult.me().setResultObj(map);

    }

    @Override
    public AjaxResult loginWechat(Map<String, String> params) {
        // 获取code和URI
        String code = params.get("code");
        String binderUrl = params.get("binderUrl");
        // 发送请求
        String getAckUrl  = WxConstants.GET_ACK_URL.replace("APPID", WxConstants.APPID).replace("SECRET", WxConstants.SECRET).replace("CODE", code);
        // 利用工具类发送请求
        String jsonStr = HttpClientUtils.httpGet(getAckUrl);
        // 转换为json对象
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
//        System.out.println(jsonObject);
        // 得到accessToken和openid
        String accessToken = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        // 查询是否关联
        WxUser wxUser = wxUserMapper.findByOpenid(openid);
        if (wxUser != null && wxUser.getUser_id() != null) {
            // 查询loginInfo对象  通过getUser_id查询
            LoginInfo loginInfo = logininfoMapper.findByUserId(wxUser.getUser_id());
            // 随机生成一个token
            // 保存在Redis中
            String token = UUID.randomUUID().toString();
            HashMap<String, Object> map = new HashMap<>();
            map.put("token", token);
            // 将密码和盐值置空
            loginInfo.setPassword(null);
            loginInfo.setSalt(null);
            map.put("loginInfo", loginInfo);
            return AjaxResult.me().setResultObj(map);
        } else {
            // 否则跳转到绑定页面
            binderUrl = binderUrl + "?accessToken=" + accessToken + "&openId=" + openid;
            return AjaxResult.me().setSuccess(false).setResultObj(binderUrl);
        }
    }

    @Override
    @Transactional
    public AjaxResult binderWechat(Map<String, String> params) {
        // 获取参数
        String phone = params.get("phone");
        // 验证码
        String verifyCode = params.get("verifyCode");
        String accessToken = params.get("accessToken");
        String openId = params.get("openId");
        // 判断非空
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(verifyCode)) {
            return AjaxResult.me().setMsg("请输入信息").setSuccess(false);
        }
        // 判断验证码是否相同
        Object codeObj  = redisTemplate.opsForValue().get(UserConstant.BINDER_CODE_PREFIX + phone);
        // 如果验证码为空
        if (codeObj == null) {
            return AjaxResult.me().setMsg("验证码失效,请重新发送!!").setSuccess(false);
        } else {
            // 获取验证码
            String verifyCodeStr = (String) codeObj;
            String code = verifyCodeStr.split(":")[0];
            // 判断验证码是否相同
            if (!code.equalsIgnoreCase(verifyCode)) {
                throw new BusinessException("验证码错误,重新输入验证码");
            }
        }
        // 获取微信用户信息
        String url = WxConstants.GET_USER_URL.replace("ACCESS_TOKEN", accessToken).replace("OPENID", openId);
        // 发送请求
        String jsonStr  = HttpClientUtils.httpGet(url);
//        System.out.println(jsonStr);
        // 查询用户是否存在
        LoginDto loginDto = new LoginDto();
        // 设置电话号码
        loginDto.setUsername(phone);
        loginDto.setType("1");
        // 查询
        LoginInfo loginInfo = logininfoMapper.findByAccount(loginDto);
        User user = null;
        if (loginInfo != null) {
            user = userMapper.findByPhone(phone);
            if (user.getState() == 0) {
                throw new BusinessException("你的账号已经被封停,请联系管理员");
            }
        } else {
            user = wxUser2User(phone);
            loginInfo = user2LoginInfo(user);
            // 保存
            logininfoMapper.save(loginInfo);
            // 设置登录信息表id
            user.setLoginInfo_id(loginInfo.getId());
            userMapper.save(user);
        }
        // 将jsonStr对象转换为wxUser对象
        WxUser wxUser = wxUserJsonStr2WxUser(jsonStr);
        // 绑定用户id
        wxUser.setUser_id(user.getId());
        // 保存用户
        wxUserMapper.save(wxUser);
        // 生成token
        String token = UUID.randomUUID().toString();
        // 保存在Redis中
        redisTemplate.opsForValue().set(token, loginInfo,30, TimeUnit.MINUTES);
        // 创建一个map对象
        HashMap<String, Object> result = new HashMap<>();
        // 存放值
        result.put("token", token);
        loginInfo.setPassword("");
        loginInfo.setSalt("");
        result.put("loginInfo", loginInfo);
        return AjaxResult.me().setResultObj(result);
    }

    @Override
    public AjaxResult loginByPhone(UserDto userDto) {
        // 判断非null
        if (StringUtils.isEmpty(userDto.getPhone()) || StringUtils.isEmpty(userDto.getVerifyCode())) {
            throw new BusinessException("手机号码或验证码不能为空");
        }
        // 判断验证是否过期,获取验证码
        Object codeObj = redisTemplate.opsForValue().get(UserConstant.LOGIN_CODE_PREFIX + userDto.getPhone());
        if (codeObj == null) {
            throw new BusinessException("验证码无效,请重新获取验证码");
        }
        // 获取之前保存的验证码
        String verifyCodeStr = (String) codeObj;
        String verifyCode = verifyCodeStr.split(":")[0];
//        System.out.println(verifyCode);
//        System.err.println(userDto);
        // 判断验证码是否相同
        if (!verifyCode.equalsIgnoreCase(userDto.getVerifyCode())) {
            throw new BusinessException("验证码错误,重新输入验证码");
        }
        // 查询用户是否注册
        // 判断手机号码是否注册
        User userTmp = userMapper.findByPhone(userDto.getPhone());
        if (userTmp == null) {
            throw new BusinessException("该手机号码还没有注册,请注册后登录");
        }
        LoginInfo loginInfo = logininfoMapper.loadById(userTmp.getLoginInfo_id());
// 生成token
        String token = UUID.randomUUID().toString();
        // 保存在Redis中
        redisTemplate.opsForValue().set(token, loginInfo,30, TimeUnit.MINUTES);
        // 创建一个map对象
        HashMap<String, Object> result = new HashMap<>();
        // 存放值
        result.put("token", token);
        loginInfo.setPassword("");
        loginInfo.setSalt("");
        result.put("loginInfo", loginInfo);
        return AjaxResult.me().setResultObj(result);
    }

    /**
     * 将获取到的微信用户信息转换为我们自己的对象
     * @param jsonStr 微信用户信息
     * @return 数据库的用户
     */
    private WxUser wxUserJsonStr2WxUser(String jsonStr) {
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        WxUser wxUser = new WxUser();
        // 进行赋值
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        wxUser.setAddress(jsonObject.getString("country") + jsonObject.getString("province") + jsonObject.getString("city"));
        wxUser.setHeadImgUrl(jsonObject.getString("headimgurl"));
        wxUser.setUnionId(jsonObject.getString("unionid"));
        return wxUser;
    }

    /**
     * 将用户转换为登录对象
     * @param user 用户
     * @return 登录对象
     */
    private LoginInfo user2LoginInfo(User user) {
        LoginInfo loginInfo = new LoginInfo();
        // 将用户信息复制给登录对象进行保存
        BeanUtils.copyProperties(user, loginInfo);
        return loginInfo;
    }

    /**
     * 将微信的信息转换为user
     * @param phone
     * @return
     */
    private User wxUser2User(String phone) {
        User user = new User();
        // 设置信息
        user.setPhone(phone);
        user.setUsername(phone);
        // 随机生成密码
        String salt = UUID.randomUUID().toString();
        String md5Pwd = MD5Utils.encrypByMd5(StrUtils.getComplexRandomString(6) + salt);
        user.setSalt(salt);
        user.setPassword(md5Pwd);
        return user;
    }
}
